package tool

import cats.data.Validated
import implicits.Implicits._
import org.apache.commons.lang3.StringUtils

/**
 * Created by Administrator on 2019/12/20
 */
class FileValidTool(lines: List[List[String]]) {

  val headers = lines.head
  val hasHeaders = List(
    "Age", "AST", "ALT", "PLT", "Tyr", "TCA", "Phe", "Trp", "Val", "CA", "Met", "GCA",
    "GCDCA", "TCDCA", "SampleID"
  )
  val fileInfo = "文件"

  def validHeadersRepeat = {
    val repeatHeaders = headers.diff(headers.distinct)
    val valid = repeatHeaders.isEmpty
    Validated.cond(valid, true, s"文件表头 ${repeatHeaders.head} 重复!")
  }

  def validHeadersHas = {
    val valid = StringUtils.isBlank(lines.head.mkString("\t"))
    Validated.cond(!valid, true, s"首行数据（表头）缺失!")
  }

  def validHeadersExist = {
    val noExistHeaders = hasHeaders.diff(headers)
    val valid = noExistHeaders.isEmpty
    Validated.cond(valid, true, s"文件表头 ${noExistHeaders.head} 不存在!")
  }

  def validAgeColumn = {
    val header = "Age"
    val totalColumns = lines.selectOneColumn(header)
    val op = totalColumns.filterNot { column =>
      column.isInt && column.toInt <= 100 && column.toInt >= 18
    }.headOption
    val inValid = op.isEmpty
    val inMessage = if (!inValid) {
      val value = op.get
      val j = headers.indexOf(header)
      val i = totalColumns.lastIndexOf(value)
      s"${fileInfo}第${i + 2}行第${j + 1}列必须为18-100之间的整数!"
    } else ""
    Validated.cond(inValid, true, inMessage)
  }

  def validAstColumn = {
    val header = "AST"
    val totalColumns = lines.selectOneColumn(header)
    val op = totalColumns.filterNot { column =>
      column.isDouble && column.toDouble >= 0 && column.toDouble <= 1000
    }.headOption
    val inValid = op.isEmpty
    val inMessage = if (!inValid) {
      val value = op.get
      val j = headers.indexOf(header)
      val i = totalColumns.lastIndexOf(value)
      s"${fileInfo}第${i + 2}行第${j + 1}列必须为0-1000之间的实数!"
    } else ""
    Validated.cond(inValid, true, inMessage).andThen { b =>
      val op = totalColumns.filterNot { column =>
        column.matches("^\\d*(\\.\\d{1,3})?$")
      }.headOption
      val inValid = op.isEmpty
      val inMessage = if (!inValid) {
        val value = op.get
        val j = headers.indexOf(header)
        val i = totalColumns.lastIndexOf(value)
        s"${fileInfo}第${i + 2}行第${j + 1}列最多保留为3位小数！"
      } else ""
      Validated.cond(inValid, true, inMessage)
    }
  }

  def validAltColumn = {
    val header = "ALT"
    val totalColumns = lines.selectOneColumn(header)
    val op = totalColumns.filterNot { column =>
      column.isDouble && column.toDouble >= 0 && column.toDouble <= 2000
    }.headOption
    val inValid = op.isEmpty
    val inMessage = if (!inValid) {
      val value = op.get
      val j = headers.indexOf(header)
      val i = totalColumns.lastIndexOf(value)
      s"${fileInfo}第${i + 2}行第${j + 1}列必须为0-2000之间的实数!"
    } else ""
    Validated.cond(inValid, true, inMessage).andThen { b =>
      val op = totalColumns.filterNot { column =>
        column.matches("^\\d*(\\.\\d{1,3})?$")
      }.headOption
      val inValid = op.isEmpty
      val inMessage = if (!inValid) {
        val value = op.get
        val j = headers.indexOf(header)
        val i = totalColumns.lastIndexOf(value)
        s"${fileInfo}第${i + 2}行第${j + 1}列最多保留为3位小数！"
      } else ""
      Validated.cond(inValid, true, inMessage)
    }
  }

  def validPltColumn = {
    val header = "PLT"
    val totalColumns = lines.selectOneColumn(header)
    val op = totalColumns.filterNot { column =>
      column.isDouble && column.toDouble >= 0 && column.toDouble <= 1000
    }.headOption
    val inValid = op.isEmpty
    val inMessage = if (!inValid) {
      val value = op.get
      val j = headers.indexOf(header)
      val i = totalColumns.lastIndexOf(value)
      s"${fileInfo}第${i + 2}行第${j + 1}列必须为0-1000之间的实数!"
    } else ""
    Validated.cond(inValid, true, inMessage).andThen { b =>
      val op = totalColumns.filterNot { column =>
        column.matches("^\\d*(\\.\\d{1,3})?$")
      }.headOption
      val inValid = op.isEmpty
      val inMessage = if (!inValid) {
        val value = op.get
        val j = headers.indexOf(header)
        val i = totalColumns.lastIndexOf(value)
        s"${fileInfo}第${i + 2}行第${j + 1}列最多保留为3位小数！"
      } else ""
      Validated.cond(inValid, true, inMessage)
    }
  }

  def validTcaColumn = {
    val header = "TCA"
    val totalColumns = lines.selectOneColumn(header)
    val op = totalColumns.filter { column =>
      !column.isDouble
    }.headOption
    val inValid = op.isEmpty
    val inMessage = if (!inValid) {
      val value = op.get
      val j = headers.indexOf(header)
      val i = totalColumns.lastIndexOf(value)
      s"${fileInfo}第${i + 2}行第${j + 1}列必须为实数!"
    } else ""
    Validated.cond(inValid, true, inMessage).andThen { b =>
      val op = totalColumns.filterNot { column =>
        column.matches("^\\d*(\\.\\d{1,3})?$")
      }.headOption
      val inValid = op.isEmpty
      val inMessage = if (!inValid) {
        val value = op.get
        val j = headers.indexOf(header)
        val i = totalColumns.lastIndexOf(value)
        s"${fileInfo}第${i + 2}行第${j + 1}列最多保留为3位小数！"
      } else ""
      Validated.cond(inValid, true, inMessage)
    }
  }

  def validPheColumn = {
    val header = "Phe"
    val totalColumns = lines.selectOneColumn(header)
    val (min, max) = (5, 1000)
    val op = totalColumns.filterNot { column =>
      column.isDouble && column.toDouble >= min && column.toDouble <= max
    }.headOption
    val inValid = op.isEmpty
    val inMessage = if (!inValid) {
      val value = op.get
      val j = headers.indexOf(header)
      val i = totalColumns.lastIndexOf(value)
      s"${fileInfo}第${i + 2}行第${j + 1}列必须为${min}-${max}之间的实数!"
    } else ""
    Validated.cond(inValid, true, inMessage).andThen { b =>
      val op = totalColumns.filterNot { column =>
        column.matches("^\\d*(\\.\\d{1,3})?$")
      }.headOption
      val inValid = op.isEmpty
      val inMessage = if (!inValid) {
        val value = op.get
        val j = headers.indexOf(header)
        val i = totalColumns.lastIndexOf(value)
        s"${fileInfo}第${i + 2}行第${j + 1}列最多保留为3位小数！"
      } else ""
      Validated.cond(inValid, true, inMessage)
    }
  }

  def validTrpColumn = {
    val header = "Trp"
    val totalColumns = lines.selectOneColumn(header)
    val (min, max) = (5, 1000)
    val op = totalColumns.filterNot { column =>
      column.isDouble && column.toDouble >= min && column.toDouble <= max
    }.headOption
    val inValid = op.isEmpty
    val inMessage = if (!inValid) {
      val value = op.get
      val j = headers.indexOf(header)
      val i = totalColumns.lastIndexOf(value)
      s"${fileInfo}第${i + 2}行第${j + 1}列必须为${min}-${max}之间的实数!"
    } else ""
    Validated.cond(inValid, true, inMessage).andThen { b =>
      val op = totalColumns.filterNot { column =>
        column.matches("^\\d*(\\.\\d{1,3})?$")
      }.headOption
      val inValid = op.isEmpty
      val inMessage = if (!inValid) {
        val value = op.get
        val j = headers.indexOf(header)
        val i = totalColumns.lastIndexOf(value)
        s"${fileInfo}第${i + 2}行第${j + 1}列最多保留为3位小数！"
      } else ""
      Validated.cond(inValid, true, inMessage)
    }
  }

  def validValColumn = {
    val header = "Val"
    val totalColumns = lines.selectOneColumn(header)
    val (min, max) = (5, 1000)
    val op = totalColumns.filterNot { column =>
      column.isDouble && column.toDouble >= min && column.toDouble <= max
    }.headOption
    val inValid = op.isEmpty
    val inMessage = if (!inValid) {
      val value = op.get
      val j = headers.indexOf(header)
      val i = totalColumns.lastIndexOf(value)
      s"${fileInfo}第${i + 2}行第${j + 1}列必须为${min}-${max}之间的实数!"
    } else ""
    Validated.cond(inValid, true, inMessage).andThen { b =>
      val op = totalColumns.filterNot { column =>
        column.matches("^\\d*(\\.\\d{1,3})?$")
      }.headOption
      val inValid = op.isEmpty
      val inMessage = if (!inValid) {
        val value = op.get
        val j = headers.indexOf(header)
        val i = totalColumns.lastIndexOf(value)
        s"${fileInfo}第${i + 2}行第${j + 1}列最多保留为3位小数！"
      } else ""
      Validated.cond(inValid, true, inMessage)
    }
  }

  def validCAColumn = {
    val header = "CA"
    val totalColumns = lines.selectOneColumn(header)
    val op = totalColumns.filter { column =>
      !column.isDouble
    }.headOption
    val inValid = op.isEmpty
    val inMessage = if (!inValid) {
      val value = op.get
      val j = headers.indexOf(header)
      val i = totalColumns.lastIndexOf(value)
      s"${fileInfo}第${i + 2}行第${j + 1}列必须为实数!"
    } else ""
    Validated.cond(inValid, true, inMessage).andThen { b =>
      val op = totalColumns.filterNot { column =>
        column.matches("^\\d*(\\.\\d{1,3})?$")
      }.headOption
      val inValid = op.isEmpty
      val inMessage = if (!inValid) {
        val value = op.get
        val j = headers.indexOf(header)
        val i = totalColumns.lastIndexOf(value)
        s"${fileInfo}第${i + 2}行第${j + 1}列最多保留为3位小数！"
      } else ""
      Validated.cond(inValid, true, inMessage)
    }
  }

  def validMetColumn = {
    val header = "Met"
    val totalColumns = lines.selectOneColumn(header)
    val (min, max) = (5, 1000)
    val op = totalColumns.filterNot { column =>
      column.isDouble && column.toDouble >= min && column.toDouble <= max
    }.headOption
    val inValid = op.isEmpty
    val inMessage = if (!inValid) {
      val value = op.get
      val j = headers.indexOf(header)
      val i = totalColumns.lastIndexOf(value)
      s"${fileInfo}第${i + 2}行第${j + 1}列必须为${min}-${max}之间的实数!"
    } else ""
    Validated.cond(inValid, true, inMessage).andThen { b =>
      val op = totalColumns.filterNot { column =>
        column.matches("^\\d*(\\.\\d{1,3})?$")
      }.headOption
      val inValid = op.isEmpty
      val inMessage = if (!inValid) {
        val value = op.get
        val j = headers.indexOf(header)
        val i = totalColumns.lastIndexOf(value)
        s"${fileInfo}第${i + 2}行第${j + 1}列最多保留为3位小数！"
      } else ""
      Validated.cond(inValid, true, inMessage)
    }
  }

  def validGCAColumn = {
    val header = "GCA"
    val totalColumns = lines.selectOneColumn(header)
    val op = totalColumns.filter { column =>
      !column.isDouble
    }.headOption
    val inValid = op.isEmpty
    val inMessage = if (!inValid) {
      val value = op.get
      val j = headers.indexOf(header)
      val i = totalColumns.lastIndexOf(value)
      s"${fileInfo}第${i + 2}行第${j + 1}列必须为实数!"
    } else ""
    Validated.cond(inValid, true, inMessage).andThen { b =>
      val op = totalColumns.filterNot { column =>
        column.matches("^\\d*(\\.\\d{1,3})?$")
      }.headOption
      val inValid = op.isEmpty
      val inMessage = if (!inValid) {
        val value = op.get
        val j = headers.indexOf(header)
        val i = totalColumns.lastIndexOf(value)
        s"${fileInfo}第${i + 2}行第${j + 1}列最多保留为3位小数！"
      } else ""
      Validated.cond(inValid, true, inMessage)
    }
  }

  def validGCDCAColumn = {
    val header = "GCDCA"
    val totalColumns = lines.selectOneColumn(header)
    val op = totalColumns.filter { column =>
      !column.isDouble
    }.headOption
    val inValid = op.isEmpty
    val inMessage = if (!inValid) {
      val value = op.get
      val j = headers.indexOf(header)
      val i = totalColumns.lastIndexOf(value)
      s"${fileInfo}第${i + 2}行第${j + 1}列必须为实数!"
    } else ""
    Validated.cond(inValid, true, inMessage).andThen { b =>
      val op = totalColumns.filterNot { column =>
        column.matches("^\\d*(\\.\\d{1,3})?$")
      }.headOption
      val inValid = op.isEmpty
      val inMessage = if (!inValid) {
        val value = op.get
        val j = headers.indexOf(header)
        val i = totalColumns.lastIndexOf(value)
        s"${fileInfo}第${i + 2}行第${j + 1}列最多保留为3位小数！"
      } else ""
      Validated.cond(inValid, true, inMessage)
    }
  }

  def validTCDCAColumn = {
    val header = "TCDCA"
    val totalColumns = lines.selectOneColumn(header)
    val op = totalColumns.filter { column =>
      !column.isDouble
    }.headOption
    val inValid = op.isEmpty
    val inMessage = if (!inValid) {
      val value = op.get
      val j = headers.indexOf(header)
      val i = totalColumns.lastIndexOf(value)
      s"${fileInfo}第${i + 2}行第${j + 1}列必须为实数!"
    } else ""
    Validated.cond(inValid, true, inMessage).andThen { b =>
      val op = totalColumns.filterNot { column =>
        column.matches("^\\d*(\\.\\d{1,3})?$")
      }.headOption
      val inValid = op.isEmpty
      val inMessage = if (!inValid) {
        val value = op.get
        val j = headers.indexOf(header)
        val i = totalColumns.lastIndexOf(value)
        s"${fileInfo}第${i + 2}行第${j + 1}列最多保留为3位小数！"
      } else ""
      Validated.cond(inValid, true, inMessage)
    }
  }

  def validTyrColumn = {
    val header = "Tyr"
    val totalColumns = lines.selectOneColumn(header)
    val (min, max) = (5, 1000)
    val op = totalColumns.filterNot { column =>
      column.isDouble && column.toDouble >= min && column.toDouble <= max
    }.headOption
    val inValid = op.isEmpty
    val inMessage = if (!inValid) {
      val value = op.get
      val j = headers.indexOf(header)
      val i = totalColumns.lastIndexOf(value)
      s"${fileInfo}第${i + 2}行第${j + 1}列必须为${min}-${max}之间的实数!"
    } else ""
    Validated.cond(inValid, true, inMessage).andThen { b =>
      val op = totalColumns.filterNot { column =>
        column.matches("^\\d*(\\.\\d{1,3})?$")
      }.headOption
      val inValid = op.isEmpty
      val inMessage = if (!inValid) {
        val value = op.get
        val j = headers.indexOf(header)
        val i = totalColumns.lastIndexOf(value)
        s"${fileInfo}第${i + 2}行第${j + 1}列最多保留为3位小数！"
      } else ""
      Validated.cond(inValid, true, inMessage)
    }
  }

  def validColumnNum = {
    val info = lines.drop(1).zipWithIndex.map { case (tmpColumns, i) =>
      val columns = tmpColumns
      val inValid = columns.size <= headers.size
      val inMessage = if (!inValid) {
        s"文件第${i + 2}行列数不正确,存在多余列!"
      } else ""
      (inValid, inMessage)
    }
    val valid = info.forall(_._1)
    val messages = info.map(_._2).notEmptyLines
    Validated.cond(valid, true, messages.head)
  }

  def validNonEmpty = {
    val info = lines.drop(1).zipWithIndex.map { case (columns, i) =>
      val lineMap = headers.zip(columns).toMap
      val op = lineMap.filter { case (header, value) =>
        hasHeaders.contains(header)
      }.values.toList.filter { column =>
        StringUtils.isEmpty(column)
      }.headOption
      val inMessage = if (op.nonEmpty) {
        val j = columns.indexOf(op.get)
        s"文件第${i + 2}行第${j + 1}列为空!"
      } else ""
      (op.isEmpty, inMessage)
    }
    val valid = info.forall(_._1)
    val messages = info.map(_._2).notEmptyLines
    Validated.cond(valid, true, messages.head)
  }

}

object FileValidTool {

  def valid(lines: List[List[String]]) = {
    val fileValidTool = new FileValidTool(lines)
    import fileValidTool._
    validHeadersHas.andThen { b =>
      validHeadersRepeat
    }.andThen { b =>
      validHeadersExist
    }.andThen { b =>
      validColumnNum
    }.andThen { b =>
      validNonEmpty
    }.andThen { b =>
      validAgeColumn
    }.andThen { b =>
      validAstColumn
    }.andThen { b =>
      validAltColumn
    }.andThen { b =>
      validPltColumn
    }.andThen { b =>
      validTyrColumn
    }.andThen { b =>
      validTcaColumn
    }.andThen { b =>
      validPheColumn
    }.andThen { b =>
      validTrpColumn
    }.andThen { b =>
      validValColumn
    }.andThen { b =>
      validCAColumn
    }.andThen { b =>
      validMetColumn
    }.andThen { b =>
      validGCAColumn
    }.andThen { b =>
      validGCDCAColumn
    }.andThen { b =>
      validTCDCAColumn
    }

  }

}
